/**
 * 
 */
package juf.adapter;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.lang.annotation.Annotation;
import java.text.ParseException;


import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import juf.Property;
import juf.Annotations.Attributes;
import juf.Annotations.Name;

/**
 * Specifies the properties and behavior common to all Adapters
 * 
 * Will be subclassed in order to wrap descendants of java.awt.Component in a lightweight JPanel that
 * 
 * 29/5 Added validation error display function
 * 
 * Extends JPanel so that concrete Adapters can be added directly to the JufForm 
 * in order to bundle interface components together with labels and action controls (button, calendar, etc)
 * 
 * @author Dale Halliwell & Parul Sharma
 *
 */
@SuppressWarnings("serial")
abstract class AbstractAdapter extends JPanel {

	protected Property property; //the Property being bound to a component
	protected Component component;
	public JLabel invalidWarningLabel; //visible if there is a problem validating
	
	/**Establishes a channel between an UI component and a data source described by the Property interface
	 * This should typically be called from in the constructor of implementing classes, but it could also 
	 * be used to dynamically reset the binding (cool).
	 * 
	 * @param property Property representing the data source to bind a component to
	 * @param defaultSize TODO
	 */
	public void bind(Property property, Dimension defaultSize) {
		this.property = property;		
		
		//if the property is readonly, the control should not be editable
		//System.out.println(this.toString() + " " + property.isReadOnly());
		if (property.isReadOnly()) {
			this.getDataComponent().setEnabled(false);
		}

		//set the layout, adding the component & label
		this.setLayout(defaultSize);
		
		//then customize the component with any annotations
		this.customize();
	
		refresh(); //update the data initial values
	}

	
	/** Displays an icon beside the adapter that shows the data was not saved because it is invalid
	 * will show until the next valid save() operation
	 */
	public void showInvalidWarning(){
		this.invalidWarningLabel.setVisible(true);
	}
	
	/** 
	 * clears any invalid warning after a successful save
	 */
	public void clearInvalidWarning(){
		this.invalidWarningLabel.setVisible(false);
	}
	/**
	 * This will be invoked by components and containers that want to add the
	 * bound component as a child, or to set properties of the component directly.
	 * 
	 * @return the bound component
	 */
	public Component getDataComponent(){
		return component;
	}

	/**
	 * This will be invoked to examine aspects of the Property, like the field name and annotations
	 * 
	 * @return the bound Property
	 */
	public Property getProperty() {
		return property;
	}

	/**
	 * Refreshes the component (view) from the property (model)
	 */
	abstract void refresh();

	/**
	 * Saves the property (model) from the component (view)
	 * @throws ParseException 
	 */
	abstract void save() throws ParseException; 
	
	
	/**
	 * this basically uses the annotations to customise the component
	 */
	private void customize() {
		Annotation[] annotations = property.getAnnotations();
		for (Annotation a : annotations){
			//get the Attributes class annotation 
			try {
				if (a.annotationType().equals(Attributes.class)){
					
					Attributes annotation = (Attributes) a;
					
					//Set Size
					if (!annotation.setSize().equals("0, 0")){
						String[] valueSplit = annotation.setSize().split(", ");
						component.setSize(Integer.parseInt(valueSplit[0]), Integer.parseInt(valueSplit[1]));
					}
					    
					//Set Columns
					if (annotation.setColumns()!=0){	
						((JTextField) component).setColumns(annotation.setColumns());
					}
					
					//Set Foreground
					if (!annotation.setForeground().equals("0, 0, 0") && getDataComponent() instanceof JTextField){
						String[] valueFore = annotation.setForeground().split(", ");
						Color c = new Color(Integer.parseInt(valueFore[0]),Integer.parseInt(valueFore[1]),Integer.parseInt(valueFore[2])); 
						((JTextField) getDataComponent()).setForeground(c);
					}
					
					//Set Enabled
					getDataComponent().setEnabled(annotation.setEnabled());
	
						
					//Set Visible
					this.setVisible(annotation.setVisible());	
						
					//Set Autoscrolls
					if (component instanceof JTextField)
						((JTextField) component).setAutoscrolls(annotation.setAutoscrolls());	
						
				}
			} catch (NumberFormatException e) {
				System.out.println("You have had an Illegal Argument Exception");
				System.out.println("The annotation value was unable to be parsed to an integer correctly");
				System.out.println("See trace below:");
				e.printStackTrace();
			}  catch (IllegalArgumentException e) {
				
				System.out.println("You have had an Illegal Argument Exception");
				System.out.println("A value that you have specified in your annotation is incorrect");
				System.out.println("See trace below:");
				e.printStackTrace();
				}
			}
		}
			
	
	
	/**
	 * Standardizes the layout of panels, so they can be stacked neatly and appear aligned 
	 * <p/>
	 * This should be overridden for complex components that require unusual layouts.
	 * @param defaultSize TODO
	 */
	protected void setLayout(Dimension defaultSize){
		this.setPreferredSize(defaultSize); //this is the default size of the adapter/panel
		
		//this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); //add some whitespace around the components
		
		//for alignment/interface testing, uncomment the next line to show the border of each panel in red
		//this.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.red),this.getBorder()));

		//set up the layout manager
        this.setLayout(new BorderLayout(5,5)); //across the page as a line of text

        //wrap the label in a fixed size jpanel to standardize label alignment
        JLabel label = getLabel();
        JPanel labelpanel = new JPanel();
        labelpanel.setLayout(new FlowLayout(FlowLayout.TRAILING));//align the labels right
        labelpanel.setPreferredSize(new Dimension(100,0)); //make the label area 100 px wide
        labelpanel.add(label); 
        
        //also wrap the component
        //TODO these alignment values may need to be tweaked
        JPanel componentpanel= new JPanel();
        componentpanel.setLayout(new FlowLayout(FlowLayout.LEADING));//align the components left
        componentpanel.setPreferredSize(new Dimension(200,0)); //make the component area 200px wide
        componentpanel.add(component);
        
        //set up the label that will show if the adapter fails to validate
        this.invalidWarningLabel = new JLabel("X"); 
        this.invalidWarningLabel.setForeground(Color.red);
        this.invalidWarningLabel.setVisible(false);
        componentpanel.add(invalidWarningLabel);
        
        //use Borderlayout for the main (adapter) panel
        this.add(labelpanel,BorderLayout.LINE_START);
        this.add(componentpanel, BorderLayout.CENTER);

	}

	
	public JLabel getLabel(){

		Annotation[] annotations = property.getAnnotations();
		for (Annotation a : annotations){
			//get the Name class annotation 
			if (a.annotationType().equals(Name.class)){
				//set the label to the text user specified
				Name nameAnno = (Name) a;
				return new JLabel(nameAnno.value());

			}
		}	
		return new JLabel(getProperty().getName()); // default label is data source name
	}


	public boolean validateField() {
		return true;
	}
	
}
